package master.ghrm.domain;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.Version;

import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.Type;
import org.joda.time.DateTime;

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Profile extends AbstractBaseEntity {

	private static final long serialVersionUID = 1L;

	public static final int LASTNAME_LENGTH = 80;
	public static final int FIRSTNAME_LENGTH = 50;
	public static final int MIDLE_NAME_LENGTH = 60;
	public static final int FULL_NAME_LENGTH = 130;
	public static final int MPCID_LENGTH = 80;
	public static final int NOTES_LENGTH = 1000;
	public static final int PID_LENGTH = 80;
	public static final int USERNAME_LENGTH = 80;
	public static final int PERSONAL_UNIQUE_NUMBER_LENGTH = 50;
	public static final int PERSONAL_CARD_INFO_LENGTH = 80;
	public static final int HEALTH_INSURANCE_INFO_LENGTH = 80;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "profile_id", unique = true, nullable = false)
	private Long profileId;

	@Version
	private Long version;

	@Column(name = "lastname", nullable = false, length = Profile.LASTNAME_LENGTH)
	private String lastname;

	@Column(name = "firstname", nullable = false, length = Profile.FIRSTNAME_LENGTH)
	private String firstname;

	@Column(name = "midle_name", length = MIDLE_NAME_LENGTH)
	private String middleName;

	@Column(name = "full_name", length = FULL_NAME_LENGTH)
	@SuppressWarnings("unused")
	private String fullName;

	@Column(name = "birth_date")
	@Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
	private DateTime birthDate;

	@Column(name = "start_work_date")
	@Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
	private DateTime startWorkDate;

	@Column(name = "end_work_date")
	@Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
	private DateTime endWorkDate;

	@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
	@JoinColumn(name = "contact_id", unique = true, updatable = true, insertable = true)
	@ForeignKey(name = "FK_PROFILE_CONTACT")
	private Contact contact;

	@Column(name = "mcpid", length = Profile.PID_LENGTH)
	private String mcpid;

	@Column(name = "scjpid", length = Profile.PID_LENGTH)
	private String scjpid;

	@Column(name = "total_years_experience", precision = 3, scale = 1)
	private BigDecimal totalYearsExperience;

	@Column(length = 1000)
	private String notes;

	@Column(name = "active")
	private Boolean active;

	@Column(nullable = false, length = Profile.USERNAME_LENGTH, unique = true)
	private String username;

	@Column(name = "personal_unique_number", length = Profile.PERSONAL_UNIQUE_NUMBER_LENGTH)
	private String personalUniqueNumber;

	@Column(name = "id_card_number", length = Profile.PERSONAL_CARD_INFO_LENGTH)
	private String idCardNumber;

	@Column(name = "id_card_issuer", length = PERSONAL_CARD_INFO_LENGTH)
	private String idCardIssuer;

	@Column(name = "id_card_issue_date")
	@Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
	private DateTime idCardIssueDate;

	@Column(name = "id_card_expiration_date")
	@Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
	private DateTime idCardExpirationDate;

	@Column(name = "health_insurance_info", length = Profile.HEALTH_INSURANCE_INFO_LENGTH)
	private String healthInsuranceInfo;

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "profile")
	private List<PersonalDevelopmentPlan> pdplans;

	@ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST })
	@JoinTable(name = "tbl_profile_role_type", joinColumns = { @JoinColumn(name = "profile_id", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "role_type_id", nullable = false, updatable = false) })
	private List<RoleType> roleTypes;

	@Column(name = "vacation_days_current_year")
	private Integer vacationDaysCurrentYear;

	@Column(name = "vacation_days_yesteryear")
	private Integer vacationDaysYesteryear;

	@Column(name = "dossier_number", nullable = true)
	private String dossierNumber;

	@Column(name = "hours_per_week")
	private BigDecimal hoursPerWeek;

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "profile")
	@OrderBy("created DESC")
	private List<Note> additionalNotes;

	public Profile(boolean nullValues) {
		super();
	}

	/**
	 * Default constructor, for persistence use only.
	 */
	public Profile() {
		profileId = null;
		pdplans = new ArrayList<PersonalDevelopmentPlan>();
		roleTypes = new ArrayList<RoleType>();
		additionalNotes = new ArrayList<Note>(0);
	}

	/**
	 * Full constructor.
	 * 
	 * @param aLastname last name.
	 * @param aFirstname first name.
	 * @param aBirthDate birth date.
	 * @param aStartWorkDate work start date.
	 * @param anUsername username.
	 */
	public Profile(String aLastname, String aFirstname, DateTime aBirthDate, DateTime aStartWorkDate, String anUsername) {
		this();
		this.lastname = aLastname;
		this.firstname = aFirstname;
		this.birthDate = aBirthDate;
		this.startWorkDate = aStartWorkDate;
		this.username = anUsername;
	}

	/**
	 * @return ID.
	 */
	public Long getProfileId() {
		return profileId;
	}

	/**
	 * For persistence use only.
	 * 
	 * @param aProfileId ID.
	 */
	public void setProfileId(Long aProfileId) {
		this.profileId = aProfileId;
	}

	/**
	 * For persistence use only.
	 * 
	 * @return entity version.
	 */
	Long getVersion() {
		return version;
	}

	/**
	 * For persistence use only.
	 * 
	 * @param aVersion entity version.
	 */
	void setVersion(Long aVersion) {
		this.version = aVersion;
	}

	/**
	 * @return last name.
	 */
	public String getLastname() {
		return lastname;
	}

	/**
	 * @param aLastname last name, up to 80 chars.
	 */
	public void setLastname(String aLastname) {
		this.lastname = aLastname;
	}

	/**
	 * @return first name.
	 */
	public String getFirstname() {
		return firstname;
	}

	/**
	 * @param aFirstname first name, up to 80 chars.
	 */
	public void setFirstname(String aFirstname) {
		this.firstname = aFirstname;
	}

	/**
	 * Calculate full name, based on which parts of it are set.
	 * 
	 * @return full name
	 */
	public String getFullName() {
		if (this.getFirstname() != null) {
			return firstname + ((lastname == null) ? "" : " " + lastname);
		}
		return (lastname == null) ? "" : lastname;
	}

	/**
	 * @param aFullName the fullName to set
	 */
	public void setFullName(String aFullName) {
		if (this.getFirstname() != null) {
			this.fullName = firstname + ((lastname == null) ? "" : " " + lastname);
		}
		this.fullName = (lastname == null) ? "" : lastname;
	}

	/**
	 * @return birth date.
	 */
	public DateTime getBirthDate() {
		return birthDate;
	}

	/**
	 * @param aBirthDate birth date.
	 */
	public void setBirthDate(DateTime aBirthDate) {
		this.birthDate = aBirthDate;
	}

	/**
	 * @return work start date
	 */
	public DateTime getStartWorkDate() {
		return startWorkDate;
	}

	/**
	 * @param aStartWorkDate work start date.
	 */
	public void setStartWorkDate(DateTime aStartWorkDate) {
		this.startWorkDate = aStartWorkDate;
	}

	/**
	 * @return work end date.
	 */
	public DateTime getEndWorkDate() {
		return endWorkDate;
	}

	/**
	 * @param anEndWorkDate work end date.
	 */
	public void setEndWorkDate(DateTime anEndWorkDate) {
		this.endWorkDate = anEndWorkDate;
	}

	/**
	 * @return contact.
	 */
	public Contact getContact() {
		return contact;
	}

	/**
	 * @param aContact contact.
	 */
	public void setContact(Contact aContact) {
		this.contact = aContact;
	}

	/**
	 * @return Microsoft Certified Professional ID.
	 */
	public String getMcpid() {
		return mcpid;
	}

	/**
	 * @param aMCPID Microsoft Certified Professional ID.
	 */
	public void setMcpid(String aMCPID) {
		this.mcpid = aMCPID;
	}

	/**
	 * @return Sun Certified Professional ID
	 */
	public String getScjpid() {
		return scjpid;
	}

	/**
	 * @param aSCJPID Sun Certified Professional ID.
	 */
	public void setScjpid(String aSCJPID) {
		this.scjpid = aSCJPID;
	}

	/**
	 * @return total years of experience.
	 */
	public BigDecimal getTotalYearsExperience() {
		return totalYearsExperience;
	}

	/**
	 * @param aTotalYearsExperience total years of experience.
	 */
	public void setTotalYearsExperience(BigDecimal aTotalYearsExperience) {
		this.totalYearsExperience = aTotalYearsExperience;
	}

	/**
	 * @return additional notes.
	 */
	public String getNotes() {
		return notes;
	}

	/**
	 * @param aNotesString additional notes, up to 1000 chars.
	 */
	public void setNotes(String aNotesString) {
		this.notes = aNotesString;
	}

	/**
	 * @return is profile active.
	 */
	public Boolean getActive() {
		return active;
	}

	/**
	 * @param isActive if is profile active.
	 */
	public void setActive(Boolean isActive) {
		this.active = isActive;
	}

	/**
	 * @return username.
	 */
	public String getUsername() {
		return username;
	}

	/**
	 * @param anUsername username, up to 80 chars.
	 */
	public void setUsername(String anUsername) {
		this.username = anUsername;
	}

	/**
	 * @return personal unique number.
	 */
	public String getPersonalUniqueNumber() {
		return personalUniqueNumber;
	}

	/**
	 * @param aPersonalUniqueNumber personal unique number, up to 50 chars.
	 */
	public void setPersonalUniqueNumber(String aPersonalUniqueNumber) {
		this.personalUniqueNumber = aPersonalUniqueNumber;
	}

	/**
	 * @return personal card info.
	 */
	public String getIdCardNumber() {
		return idCardNumber;
	}

	/**
	 * @param aIdCardNumber personal card info, up to 80 chars.
	 */
	public void setIdCardNumber(String aIdCardNumber) {
		this.idCardNumber = aIdCardNumber;
	}

	/**
	 * @return health insurance info.
	 */
	public String getHealthInsuranceInfo() {
		return healthInsuranceInfo;
	}

	/**
	 * @param aHealthInsuranceInfo health insurance info, 80 chars.
	 */
	public void setHealthInsuranceInfo(String aHealthInsuranceInfo) {
		this.healthInsuranceInfo = aHealthInsuranceInfo;
	}

	/**
	 * @return list of PDPs
	 */
	public List<PersonalDevelopmentPlan> getPdplans() {
		return pdplans;
	}

	/**
	 * @param pdplanList PDPs
	 */
	public void setPdplans(List<PersonalDevelopmentPlan> pdplanList) {
		this.pdplans = pdplanList;
	}

	/**
	 * @param pdplan PDP to add.
	 */
	public void addPdplans(PersonalDevelopmentPlan pdplan) {
		if (pdplan != null) {
			pdplans.add(pdplan);
		}
	}

	/**
	 * @return role types.
	 */
	public List<RoleType> getRoleTypes() {
		return roleTypes;
	}

	/**
	 * @param roleTypeList role types.
	 */
	public void setRoleTypes(List<RoleType> roleTypeList) {
		this.roleTypes = roleTypeList;
	}

	public void addRoleType(RoleType roleType) {
		if (this.roleTypes == null) {
			this.roleTypes = new ArrayList<RoleType>(0);
		}
		if (roleType != null && !roleTypes.contains(roleType)) {
			this.roleTypes.add(roleType);
		}
	}

	/**
	 * @return the vacationDaysCurrentYear
	 */
	public Integer getVacationDaysCurrentYear() {
		return vacationDaysCurrentYear;
	}

	/**
	 * @param aVacationDaysCurrentYear the vacationDaysCurrentYear to set
	 */
	public void setVacationDaysCurrentYear(Integer aVacationDaysCurrentYear) {
		this.vacationDaysCurrentYear = aVacationDaysCurrentYear;
	}

	/**
	 * @return the vacationDaysYesteryear
	 */
	public Integer getVacationDaysYesteryear() {
		return vacationDaysYesteryear;
	}

	/**
	 * @param aVacationDaysYesteryear the vacationDaysYesteryear to set
	 */
	public void setVacationDaysYesteryear(Integer aVacationDaysYesteryear) {
		this.vacationDaysYesteryear = aVacationDaysYesteryear;
	}

	/**
	 * @return the dossierNumber
	 */
	public String getDossierNumber() {
		return dossierNumber;
	}

	/**
	 * @param aDossierNumber the dossierNumber to set
	 */
	public void setDossierNumber(String aDossierNumber) {
		this.dossierNumber = aDossierNumber;
	}

	/**
	 * @return the hoursPerWeek
	 */
	public BigDecimal getHoursPerWeek() {
		return hoursPerWeek;
	}

	/**
	 * @param weekHours the hoursPerWeek to set
	 */
	public void setHoursPerWeek(BigDecimal weekHours) {
		this.hoursPerWeek = weekHours;
	}

	/**
	 * @return the middleName
	 */
	public String getMiddleName() {
		return middleName;
	}

	/**
	 * @param aMiddleName the middleName to set
	 */
	public void setMiddleName(String aMiddleName) {
		this.middleName = aMiddleName;
	}

	/**
	 * @return the idCardIssuer
	 */
	public String getIdCardIssuer() {
		return idCardIssuer;
	}

	/**
	 * @param anIdCardIssuer the idCardIssuer to set
	 */
	public void setIdCardIssuer(String anIdCardIssuer) {
		this.idCardIssuer = anIdCardIssuer;
	}

	/**
	 * @return the idCardIssueDate
	 */
	public DateTime getIdCardIssueDate() {
		return idCardIssueDate;
	}

	/**
	 * @param anIdCardIssueDate the idCardIssueDate to set
	 */
	public void setIdCardIssueDate(DateTime anIdCardIssueDate) {
		this.idCardIssueDate = anIdCardIssueDate;
	}

	/**
	 * @return the idCardExpirationDate
	 */
	public DateTime getIdCardExpirationDate() {
		return idCardExpirationDate;
	}

	/**
	 * @param anIdCardExpirationDate the idCardExpirationDate to set
	 */
	public void setIdCardExpirationDate(DateTime anIdCardExpirationDate) {
		this.idCardExpirationDate = anIdCardExpirationDate;
	}

	/**
	 * @return list of additional notes.
	 */
	public List<Note> getAdditionalNotes() {
		return additionalNotes;
	}

	/**
	 * @param additionalNotesList additional notes to set.
	 */
	public void setAdditionalNotes(List<Note> additionalNotesList) {
		this.additionalNotes = additionalNotesList;
	}

	public boolean hasRoleType(String roleName) {
		for (RoleType role : getRoleTypes()) {
			if (roleName.equals(role.getName())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * @see com.levi9.hrm.model.base.AbstractBaseEntity#getExcludedAttributes()
	 */
	@Override
	protected String[] getExcludedAttributes() {
		return new String[] { "addresses", "dependants", "passports", "attachments", "educations", "languages",
				"profileOrganizationRoles", "contracts", "pdplans", "skillGrades", "roleTypes", "interviews",
				"customSkills", "leaveRequests", "holidayGroups", "organizationStructures", "additionalNotes" };
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = super.hashCode();
		result = prime * result + ((profileId == null) ? 0 : profileId.hashCode());
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		Profile other = (Profile) obj;
		if (profileId == null) {
			if (other.profileId != null) {
				return false;
			}
		} else if (!profileId.equals(other.profileId)) {
			return false;
		}
		return true;
	}

}
